package com.xz.questions.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xz.questions.common.ErrorCode;
import com.xz.questions.constant.CommonConstant;
import com.xz.questions.exception.BusinessException;
import com.xz.questions.exception.ThrowUtils;
import com.xz.questions.mapper.QuestionBankQuestionMapper;
import com.xz.questions.model.dto.questionBankQuestion.QuestionBankQuestionQueryRequest;
import com.xz.questions.model.entity.Question;
import com.xz.questions.model.entity.QuestionBank;
import com.xz.questions.model.entity.QuestionBankQuestion;
import com.xz.questions.model.entity.User;
import com.xz.questions.model.vo.QuestionBankQuestionVO;
import com.xz.questions.model.vo.UserVO;
import com.xz.questions.service.QuestionBankQuestionService;
import com.xz.questions.service.QuestionBankService;
import com.xz.questions.service.QuestionService;
import com.xz.questions.service.UserService;
import com.xz.questions.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 题库题目关联服务实现
 */
@Service
@Slf4j
public class QuestionBankQuestionServiceImpl extends ServiceImpl<QuestionBankQuestionMapper, QuestionBankQuestion> implements QuestionBankQuestionService {

    @Resource
    private UserService userService;

    @Resource
    @Lazy
    private QuestionService questionService;

    @Resource
    private QuestionBankService questionBankService;

    /**
     * 校验数据
     *
     * @param questionBankQuestion
     * @param add                  对创建的数据进行校验
     */
    @Override
    public void validQuestionBankQuestion(QuestionBankQuestion questionBankQuestion, boolean add) {
        ThrowUtils.throwIf(questionBankQuestion == null, ErrorCode.PARAMS_ERROR);
        // todo 从对象中取值
        Long questionId = questionBankQuestion.getQuestionId();
        if (questionId != null) {
            Question question = questionService.getById(questionId);
            if (question == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
            }
        }

        Long questionBankId = questionBankQuestion.getQuestionBankId();
        if (questionBankId != null) {
            QuestionBank questionBank = questionBankService.getById(questionBankId);
            if (questionBank == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题库不存在");
            }
        }


        // String title = questionBankQuestion.getTitle();
        // // 创建数据时，参数不能为空
        // if (add) {
        //     // todo 补充校验规则
        //     ThrowUtils.throwIf(StringUtils.isBlank(title), ErrorCode.PARAMS_ERROR);
        // }
        // // 修改数据时，有参数则校验
        // // todo 补充校验规则
        // if (StringUtils.isNotBlank(title)) {
        //     ThrowUtils.throwIf(title.length() > 80, ErrorCode.PARAMS_ERROR, "标题过长");
        // }
    }

    /**
     * 获取查询条件
     *
     * @param questionBankQuestionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<QuestionBankQuestion> getQueryWrapper(QuestionBankQuestionQueryRequest questionBankQuestionQueryRequest) {
        QueryWrapper<QuestionBankQuestion> queryWrapper = new QueryWrapper<>();
        if (questionBankQuestionQueryRequest == null) {
            return queryWrapper;
        }
        // todo 从对象中取值
        Long id = questionBankQuestionQueryRequest.getId();

        String sortField = questionBankQuestionQueryRequest.getSortField();
        String sortOrder = questionBankQuestionQueryRequest.getSortOrder();

        Long userId = questionBankQuestionQueryRequest.getUserId();
        // todo 补充需要的查询条件
        // // 从多字段中搜索
        // if (StringUtils.isNotBlank(searchText)) {
        //     // 需要拼接查询条件
        //     queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
        // }
        // // 模糊查询
        // queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
        // queryWrapper.like(StringUtils.isNotBlank(content), "content", content);
        // // JSON 数组查询
        // if (CollUtil.isNotEmpty(tagList)) {
        //     for (String tag : tagList) {
        //         queryWrapper.like("tags", "\"" + tag + "\"");
        //     }
        // }
        // // 精确查询
        // queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题库题目关联封装
     *
     * @param questionBankQuestion
     * @param request
     * @return
     */
    @Override
    public QuestionBankQuestionVO getQuestionBankQuestionVO(QuestionBankQuestion questionBankQuestion, HttpServletRequest request) {
        // 对象转封装类
        QuestionBankQuestionVO questionBankQuestionVO = QuestionBankQuestionVO.objToVo(questionBankQuestion);

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long userId = questionBankQuestion.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        questionBankQuestionVO.setUser(userVO);
        // 2. 已登录，获取用户点赞、收藏状态
        // long questionBankQuestionId = questionBankQuestion.getId();
        // User loginUser = userService.getLoginUserPermitNull(request);
        // if (loginUser != null) {
        //     // 获取点赞
        //     QueryWrapper<QuestionBankQuestionThumb> questionBankQuestionThumbQueryWrapper = new QueryWrapper<>();
        //     questionBankQuestionThumbQueryWrapper.in("questionBankQuestionId", questionBankQuestionId);
        //     questionBankQuestionThumbQueryWrapper.eq("userId", loginUser.getId());
        //     QuestionBankQuestionThumb questionBankQuestionThumb = questionBankQuestionThumbMapper.selectOne(questionBankQuestionThumbQueryWrapper);
        //     questionBankQuestionVO.setHasThumb(questionBankQuestionThumb != null);
        //     // 获取收藏
        //     QueryWrapper<QuestionBankQuestionFavour> questionBankQuestionFavourQueryWrapper = new QueryWrapper<>();
        //     questionBankQuestionFavourQueryWrapper.in("questionBankQuestionId", questionBankQuestionId);
        //     questionBankQuestionFavourQueryWrapper.eq("userId", loginUser.getId());
        //     QuestionBankQuestionFavour questionBankQuestionFavour = questionBankQuestionFavourMapper.selectOne(questionBankQuestionFavourQueryWrapper);
        //     questionBankQuestionVO.setHasFavour(questionBankQuestionFavour != null);
        // }
        // endregion

        return questionBankQuestionVO;
    }

    /**
     * 分页获取题库题目关联封装
     *
     * @param questionBankQuestionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionBankQuestionVO> getQuestionBankQuestionVOPage(Page<QuestionBankQuestion> questionBankQuestionPage, HttpServletRequest request) {
        List<QuestionBankQuestion> questionBankQuestionList = questionBankQuestionPage.getRecords();
        Page<QuestionBankQuestionVO> questionBankQuestionVOPage = new Page<>(questionBankQuestionPage.getCurrent(), questionBankQuestionPage.getSize(), questionBankQuestionPage.getTotal());
        if (CollUtil.isEmpty(questionBankQuestionList)) {
            return questionBankQuestionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionBankQuestionVO> questionBankQuestionVOList = questionBankQuestionList.stream().map(questionBankQuestion -> {
            return QuestionBankQuestionVO.objToVo(questionBankQuestion);
        }).collect(Collectors.toList());

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Set<Long> userIdSet = questionBankQuestionList.stream().map(QuestionBankQuestion::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // // 2. 已登录，获取用户点赞、收藏状态
        // Map<Long, Boolean> questionBankQuestionIdHasThumbMap = new HashMap<>();
        // Map<Long, Boolean> questionBankQuestionIdHasFavourMap = new HashMap<>();
        // User loginUser = userService.getLoginUserPermitNull(request);
        // if (loginUser != null) {
        //     Set<Long> questionBankQuestionIdSet = questionBankQuestionList.stream().map(QuestionBankQuestion::getId).collect(Collectors.toSet());
        //     loginUser = userService.getLoginUser(request);
        //     // 获取点赞
        //     QueryWrapper<QuestionBankQuestionThumb> questionBankQuestionThumbQueryWrapper = new QueryWrapper<>();
        //     questionBankQuestionThumbQueryWrapper.in("questionBankQuestionId", questionBankQuestionIdSet);
        //     questionBankQuestionThumbQueryWrapper.eq("userId", loginUser.getId());
        //     List<QuestionBankQuestionThumb> questionBankQuestionQuestionBankQuestionThumbList = questionBankQuestionThumbMapper.selectList(questionBankQuestionThumbQueryWrapper);
        //     questionBankQuestionQuestionBankQuestionThumbList.forEach(questionBankQuestionQuestionBankQuestionThumb -> questionBankQuestionIdHasThumbMap.put(questionBankQuestionQuestionBankQuestionThumb.getQuestionBankQuestionId(), true));
        //     // 获取收藏
        //     QueryWrapper<QuestionBankQuestionFavour> questionBankQuestionFavourQueryWrapper = new QueryWrapper<>();
        //     questionBankQuestionFavourQueryWrapper.in("questionBankQuestionId", questionBankQuestionIdSet);
        //     questionBankQuestionFavourQueryWrapper.eq("userId", loginUser.getId());
        //     List<QuestionBankQuestionFavour> questionBankQuestionFavourList = questionBankQuestionFavourMapper.selectList(questionBankQuestionFavourQueryWrapper);
        //     questionBankQuestionFavourList.forEach(questionBankQuestionFavour -> questionBankQuestionIdHasFavourMap.put(questionBankQuestionFavour.getQuestionBankQuestionId(), true));
        // }
        // // 填充信息
        // questionBankQuestionVOList.forEach(questionBankQuestionVO -> {
        //     Long userId = questionBankQuestionVO.getUserId();
        //     User user = null;
        //     if (userIdUserListMap.containsKey(userId)) {
        //         user = userIdUserListMap.get(userId).get(0);
        //     }
        //     questionBankQuestionVO.setUser(userService.getUserVO(user));
        //     questionBankQuestionVO.setHasThumb(questionBankQuestionIdHasThumbMap.getOrDefault(questionBankQuestionVO.getId(), false));
        //     questionBankQuestionVO.setHasFavour(questionBankQuestionIdHasFavourMap.getOrDefault(questionBankQuestionVO.getId(), false));
        // });
        // endregion

        questionBankQuestionVOPage.setRecords(questionBankQuestionVOList);
        return questionBankQuestionVOPage;
    }

    /**
     * 批量添加题库题目关联
     *
     * @param questionIdList 题目 id 集合
     * @param questionBankId 题库 id
     * @param loginUser      登录用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddQuestionBankQuestion(List<Long> questionIdList, Long questionBankId, User loginUser) {
        // 校验参数
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIdList), ErrorCode.PARAMS_ERROR, "题目列表不能为空");
        ThrowUtils.throwIf(questionBankId == null, ErrorCode.PARAMS_ERROR, "题库 id 不能为空");

        // 判断题目是否存在
        List<Question> questionList = questionService.listByIds(questionIdList);
        ThrowUtils.throwIf(CollUtil.isEmpty(questionList), ErrorCode.NOT_FOUND_ERROR, "题目不存在");

        Set<Long> validQuestionList = questionList.stream()
                .map(Question::getId)
                .collect(Collectors.toSet());
        ThrowUtils.throwIf(CollUtil.isEmpty(validQuestionList), ErrorCode.NOT_FOUND_ERROR, "合法题目列表id为空");

        // 判断哪些题目存在于题库中
        LambdaQueryWrapper<QuestionBankQuestion> wrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                .eq(QuestionBankQuestion::getQuestionBankId, questionBankId)
                .in(QuestionBankQuestion::getQuestionId, validQuestionList);

        // 存在于题库中的数据
        List<QuestionBankQuestion> existQuestion = this.list(wrapper);
        validQuestionList = existQuestion.stream()
                .map(QuestionBankQuestion::getId)
                .collect(Collectors.toSet());

        // 过滤已存在于题库中的数据
        validQuestionList = validQuestionList.stream()
                        .filter(questionId -> !questionIdList.contains(questionId)).collect(Collectors.toSet());
        ThrowUtils.throwIf(CollUtil.isEmpty(validQuestionList), ErrorCode.NOT_FOUND_ERROR, "所有题目都存在题库中");

        // 查询题库是否存在
        QuestionBank questionBank = questionBankService.getById(questionBankId);
        ThrowUtils.throwIf(questionBank == null, ErrorCode.NOT_FOUND_ERROR, "题库为空");

        // 封装
        List<QuestionBankQuestion> list = new LinkedList<>();

        // 分批处理，避免长事务
        validQuestionList.forEach(questionId -> {
            QuestionBankQuestion questionBankQuestion = new QuestionBankQuestion();
            questionBankQuestion.setQuestionBankId(questionBankId);
            questionBankQuestion.setQuestionId(questionId);
            questionBankQuestion.setUserId(loginUser.getId());
            list.add(questionBankQuestion);
        });

        // 保存
        this.saveBatch(list);
    }


    /**
     * 内部使用，避免长事务
     * @param questionBankQuestions
     */
    // @Transactional(rollbackFor = Exception.class)
    // public void batchAddQuestionBankQuestionInner(List<QuestionBankQuestion> questionBankQuestions) {
    //     for (QuestionBankQuestion questionBankQuestion : questionBankQuestions) {
    //         Long questionBankId = questionBankQuestion.getQuestionBankId();
    //         Long questionId = questionBankQuestion.getQuestionId();
    //         try {
    //             boolean result = this.save(questionBankQuestion);
    //             if (!result) {
    //                 throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
    //             }
    //         } catch (DataIntegrityViolationException e) {
    //             log.error("数据库唯一键冲突或违反其他完整性约束，题目 id: {}, 题库 id: {}, 错误信息: {}",
    //                     questionId, questionBankId, e.getMessage());
    //             throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目已存在于该题库，无法重复添加");
    //         } catch (DataAccessException e) {
    //             log.error("数据库连接问题、事务问题等导致操作失败，题目 id: {}, 题库 id: {}, 错误信息: {}",
    //                     questionId, questionBankId, e.getMessage());
    //             throw new BusinessException(ErrorCode.OPERATION_ERROR, "数据库操作失败");
    //         } catch (Exception e) {
    //             // 捕获其他异常，做通用处理
    //             log.error("添加题目到题库时发生未知错误，题目 id: {}, 题库 id: {}, 错误信息: {}",
    //                     questionId, questionBankId, e.getMessage());
    //             throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
    //         }
    //
    //     }
    //
    // }

    /**
     * 批量移除题库题目关联
     * @param questionIdList 题目 id 集合
     * @param questionBankId 题库 id
     */
    @Override
    public void batchRemoveQuestionBankQuestion(List<Long> questionIdList, Long questionBankId) {
        // 校验参数
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIdList), ErrorCode.PARAMS_ERROR, "题目列表不能为空");
        ThrowUtils.throwIf(questionBankId == null, ErrorCode.PARAMS_ERROR, "题库 id 不能为空");


        questionIdList.forEach(questionId -> {
            LambdaQueryWrapper<QuestionBankQuestion> wrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionBankId, questionBankId)
                    .eq(QuestionBankQuestion::getQuestionId, questionId);
            this.remove(wrapper);
        });


    }

}
